#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

#define TTY1 "/dev/tty11"
#define TTY2 "/dev/tty12"
#define BUF_SIZE 4096
#define JOB_MAX 1024

#define set_fl(fd, fl)                           \
    if (fcntl(fd, F_SETFL, fl | O_NONBLOCK) < 0) \
    {                                            \
        perror("fcntl fset fl");                 \
        exit(1);                                 \
    } 

enum
{
    STATE_R = 1,
    STATE_W,
    STATE_EX,
    STATE_T
};

struct fsm_st
{
    int state;
    int src;
    int dst;
    char buf[BUF_SIZE];
    ssize_t buf_len;
    char* cursor;
};

static void fsm_driver(struct fsm_st* fsm) {
    switch (fsm->state)
    {
    case STATE_R: {
        // read content into buffer
        ssize_t len = read(fsm->src, fsm->buf, BUF_SIZE);
        if(len < 0) {
            // error occurred, if error is err int or err nonblock, do nothing
            if(errno == EAGAIN) {
                break;
            }
            fsm->state = STATE_EX;
            break;
        }
        if(len == 0) {
            // terminated
            fsm->state = STATE_T;
            break;
        }

        fsm->buf_len = len;
        fsm->state = STATE_W;
        fsm->cursor = fsm->buf;
        break;        
    }

    case STATE_W: {
        ssize_t len = write(fsm->dst, fsm->cursor, fsm->buf_len);
        if (len < 0) {
            // error occurred, if error is err int or err nonblock, do nothing
            if(errno == EAGAIN) {
                break;
            }
            fsm->state = STATE_EX;
            break;            
        }

        fsm->buf_len -= len;
        fsm->cursor += len;
        if(fsm->buf_len == 0) {
            fsm->state = STATE_R;
        }
        break;
    }
    case STATE_EX: {
        perror("state ex");
        close(fsm->src);
        close(fsm->dst);
        exit(1);
        break;
    } 
    
    default:
        break;
    }
}

static void relay(int fd1, int fd2)
{
    // set NONBLOCK flag
    int fd1_sv = fcntl(fd1, F_GETFL);
    int fd2_sv = fcntl(fd2, F_GETFL);

    set_fl(fd1, fd1_sv | O_NONBLOCK);
    set_fl(fd2, fd2_sv | O_NONBLOCK);

    struct fsm_st fsm12;
    struct fsm_st fsm21;

    fsm12.src = fd1;
    fsm12.dst = fd2;
    fsm12.state = STATE_R;

    fsm21.src = fd2;
    fsm21.dst = fd1;
    fsm21.state = STATE_R;

    while(fsm12.state != STATE_T && fsm21.state != STATE_T) {
        fsm_driver(&fsm12);
        fsm_driver(&fsm21);
    }


    set_fl(fd1, fd1_sv);
    set_fl(fd2, fd2_sv);
}

enum {
    STATE_RUNNING = 1,
    STATE_CANCELED,
    STATE_OVER,
};

struct rel_stat_st {
    int state;
    int fd1;
    int fd2;
    struct timeval start;
    struct timeval end;
};

int rel_addjob(int fd1, int fd2) {
    return 0;
}

int rel_canceljob(int id) {
    return 0;
}

int rel_waitjob(int id) {
    return 0;
}

int main() {
    exit(0);
}